home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CDSupport / UpDateCopy / Source / UpDateCopy.e
Text File  |  1998-03-17  |  31KB  |  975 lines

  1. /* UpDateCopy.e
  2. ** Copies multiple files/directories to one destination.
  3. ** Not existing directories are created.
  4. ** Already existing files are only replaced by newer ones
  5. ** (UpDateCopy first checks for versions-string and then
  6. **  for dates).
  7. **
  8. ** $VER: UpDateCopy.e 1.5 (17.03.98)
  9. **
  10. ** This program is Cardware. If you use it you should send an Email to
  11. ** the author. Also small presents are very welcome.
  12. ** You may use this sourcefile or parts of it freely in your programs.
  13. ** But please do not spread a modified version under this name (UpDateCopy).
  14. ** For Bugreports, ideas or anything else send a Email to:
  15. **    ss37@irz.inf.tu-dresden.de
  16. ** 
  17. ** Sven Steiniger, 1996-98
  18. **
  19. ** Fold-start: ->// ""
  20. ** Fold-stop:  ->\\
  21. **
  22. ** History:
  23. **  1.5 : - anchor structure was not longword aligned
  24. **      : - added lha-compatible date compare mode
  25. **  1.4 : - FIXED: directories may be created, even in testmode
  26. **      : - FIXED: protection bits may be modified in testmode
  27. **  1.3 : - the file-io buffer is now allocated once at start
  28. **      . - cleaned up
  29. **  1.2 : - fixed small bug in getVersion()
  30. **  1.1 : - uses now asyncio.library for fantastic speed
  31. **  1.00: - implemented SAB, CAB options
  32. **  0.54: - implemented NOASK-option
  33. **  0.53: - now user is asked on errors. Doesn't works perfect but I don't now
  34. **          how to implement it properly without adding to much extra code.
  35. **  0.52: - improved SMARTINFO-option
  36. **  0.51: - removed bug of version-scan-routines
  37. **  0.50: - added FAST-option
  38. **  0.49: - added SMARTINFO option
  39. **        - added wildcard-check. Now you can also use '*' as wildcard and
  40. **          you can also pass devices as sources (eq. ram:)
  41. **  0.48: - used exthelp-feature OF ReadArgs()
  42. **        - no longer needs 'c:copy'-command. Implemented an own routine.
  43. **  0.47: - file was not closed before SetProtection()/SetFileDate()
  44. **  0.46: - added PAPF-option -> archive protection flag is not changed
  45. **  0.45: - added CLONE-option -> Datestamp is copied
  46. **  0.44: - removed a unnecessary module. Saves 1K of executable size!
  47. **        - removed unnecessary ExamineFH()
  48. **        - now file gets only examined if really necessary
  49. **        - optimized a bit
  50. **        - FIXED: TESTMODE was buggy -> Directories were still created.
  51. **  0.43: - add new option TESTMODE; dont copy/replace files
  52. **  0.42: - add new option NODATECHECK; dont compare datestamps
  53. **  0.41: - replaced all reads/writes by fread/fwrite
  54. **  0.40: - first public release
  55. */
  56.  
  57. OPT OSVERSION=37
  58. OPT REG=5
  59.  
  60. MODULE 'dos/dos','dos/dosasl','dos/dostags','dos/rdargs','dos/stdio',
  61.        'asyncio','libraries/asyncio'
  62. MODULE 'sven/anchorPath',
  63.        'sven/eAddPart'
  64.  
  65. RAISE "MEM"  IF String()=NIL,
  66.       "^C"   IF CtrlC()=TRUE,
  67.       "copy" IF FileLength()=-1,
  68.       "MEM"  IF AllocDosObject()=NIL,
  69.       "fatt" IF SetProtection()=DOSFALSE,
  70.       "fatt" IF SetComment()=DOSFALSE,
  71.       "fatt" IF SetFileDate()=DOSFALSE
  72.  
  73.  
  74. CONST MAXPATH=256,                -> Maximum path length
  75.       SPACEADD=3,                 -> Number of spaces per indent
  76.       SAFETYBYTES=100,            -> Maximum length of versionstring
  77.       BIGFILEMEM=16384            -> Files bigger than that are not read
  78.                                   -> completly into memory
  79.  
  80. CONST PATHLENGTH=MAXPATH-1,
  81.       BIGFILESIZE=BIGFILEMEM-SAFETYBYTES
  82.  
  83. CONST ASYNCBUFFER=BIGFILESIZE*2
  84.                                   
  85.  
  86. ENUM PDIR_Error,
  87.      PDIR_Skipped,
  88.      PDIR_Created
  89.  
  90. ENUM PFILE_Error,
  91.      PFILE_Skipped,
  92.      PFILE_Copied,
  93.      PFILE_Replaced
  94.  
  95. ENUM ASKUSER_Abort,
  96.      ASKUSER_Retry,
  97.      ASKUSER_Skip
  98.  
  99. DEF frompath[MAXPATH]:STRING,     -> actual sourcepath
  100.     fromlist:PTR TO LONG,         -> pointer to array of sourcestrings
  101.     topath[MAXPATH]:STRING,       -> destinationpath
  102.     doinfo,                       -> should we write informations ?
  103.     recursiv,                     -> scan recursively through subdirectories ?
  104.     ignoreprotection,             -> clear delete-protection ?
  105.     checkversion,                 -> compare version-strings ?
  106.     checkdates,                   -> compare datestamps ?
  107.     testmode,                     -> test modus ? (do not copy/replace files)
  108.     clone,                        -> copy datestamp ?
  109.     cleararchivebit,              -> clear archivebit OF copied file
  110.     smartinfo,                    -> only display copied/replaced files
  111.     fastdisplay,                  -> no linefeed
  112.     interaction,                  -> ask user on errors
  113.     setarchivebit,                -> set Archive bit
  114.     checkarchivebit,              -> check Archive bit
  115.     lousydatestamp,               -> lha compatible datestamp compare
  116.     dirlock=NIL                   -> PTR TO lock-structure OF destination path
  117.  
  118. DEF buffer=NIL:PTR TO CHAR        -> the global buffer used for all file-io.
  119.                                   -> Contains BIGFILEMEM+8 Bytes
  120.  
  121. PROC main() HANDLE
  122. ->// "main()"
  123. DEF rdargs=NIL,
  124.     myargs:PTR TO LONG,
  125.     template,
  126.     myrdargs=NIL:PTR TO rdargs,
  127.     exthelp[1000]:STRING
  128.  
  129.   asynciobase:=OpenLibrary('asyncio.library',39)
  130.   IF asynciobase=NIL THEN Raise("asio")
  131.  
  132.   /* Initialize argument-array */
  133.   myargs:=[
  134.            NIL,           -> fromlist
  135.            NIL,           -> destination path
  136.            FALSE,         -> ~showinfo
  137.            FALSE,         -> scan recursiv throuhg subdirectories
  138.            FALSE,         -> ignore protectionbits
  139.            FALSE,         -> deep scan
  140.            FALSE,         -> ~compare datestamps
  141.            FALSE,         -> testmode
  142.            FALSE,         -> clone
  143.            FALSE,         -> preserve archive protection flag
  144.            FALSE,         -> smartinfo
  145.            FALSE,         -> fastdisplay
  146.            FALSE,         -> interaction
  147.            FALSE,         -> set Archivebit
  148.            FALSE,         -> check Archivbit
  149.            FALSE          -> precise datestamp
  150.           ]:LONG    
  151.  
  152.   myrdargs:=AllocDosObject(DOS_RDARGS,NIL)
  153.  
  154.   StrCopy(exthelp, '\n'+
  155.                    '  UpDateCopy v1.5\n'+
  156.                    ' -----------------\n'+
  157.                    '  Copy files and directories.\n'+
  158.                    '  Not existing directories are created and\n'+
  159.                    '  already existing files are only replaced\n'+
  160.                    '  by newer ones. Allows pattern-matching and\n'+
  161.                    '  multiple sourcedirectories/files.\n\n'+
  162.                    'Sven Steiniger, 1996-98\n'
  163.         )
  164.   StrAdd(exthelp,  '\n'+
  165.                    '  FROM        - source directory/file(s)\n'+
  166.                    '  TO          - destination directory\n'+
  167.                    '  QUIET       - no outputs\n'+
  168.                    '  ALL         - scans through subdirectories recursively\n'+
  169.                    '  FORCE       - ignore delete-protectionbit\n'+
  170.                    '  DEEP        - compare version-strings\n'+
  171.                    '  NODATECHECK - don''t compare datestamps\n'+
  172.                    '  TESTMODE    - neither copy/replace files nor create directories\n'+
  173.                    '  CLONE       - copy datestamp\n'+
  174.                    '  PAB         - don''t clear the archive protection flag\n'+
  175.                    '  SMARTINFO   - only displays copied/replaced files\n'+
  176.                    '  FAST        - fast output. No linefeeds\n'+
  177.                    '  NOASK       - don''t ask user on errors\n'+
  178.                    '  SETARCBIT   - set archive-bit on copied directories/files\n'+
  179.                    '  CHECKARCBIT - only copy files with no archive-bit\n'+
  180.                    '  PRECISEDS   - compare datestamps precisly\n'
  181.         )
  182.   myrdargs.exthelp:=exthelp
  183.  
  184.   /* Parse Commandline */
  185.   IF rdargs:=ReadArgs(template:='FROM/M,TO/A,QUIET/S,ALL/S,FORCE/S,DEEP/S,'+
  186.                                 'NDC=NODATECHECK/S,TESTMODE/S,CLONE/S,PAB=PAPF/S,'+
  187.                                 'SMARTINFO/S,FAST/S,NOASK/S,SAB=SETARCBIT/S,'+
  188.                                 'CAB=CHECKARCBIT/S,PRECISEDS/S',
  189.                       myargs,myrdargs)
  190.  
  191.     /* Copy Datas to global variables */
  192.     fromlist         := myargs[0]
  193.     StrCopy(topath,myargs[1])
  194.     doinfo           := Not(myargs[2])
  195.     recursiv         := myargs[3]
  196.     ignoreprotection := myargs[4]
  197.     checkversion     := myargs[5]
  198.     checkdates       := Not(myargs[6])
  199.     testmode         := myargs[7]
  200.     clone            := myargs[8]
  201.     cleararchivebit  := Not(myargs[9])
  202.     smartinfo        := myargs[10]
  203.     fastdisplay      := myargs[11]
  204.     interaction      := Not(myargs[12])
  205.     setarchivebit    := myargs[13]
  206.     checkarchivebit  := myargs[14]
  207.     lousydatestamp   := Not(myargs[15])
  208.  
  209.     IF fromlist=NIL THEN Throw("ARGS",'No source specified.')
  210.     init_arguments()
  211.  
  212.     IF doinfo
  213.       PrintF('Updating files to "\s".\n',topath)
  214.       IF testmode THEN PrintF('** Testmode **\n')
  215.     ENDIF
  216.  
  217.     IF fastdisplay
  218.       PrintF('\n')
  219.       smartinfo:=FALSE
  220.     ENDIF
  221.  
  222.     buffer:=NewR(BIGFILEMEM+8)           -> Allocate file-io buffer
  223.  
  224.     WHILE fromlist[]                     -> 'fromlist' is array of string-pointers
  225.       StrCopy(frompath,fromlist[]++)     -> copy the string and increment 'fromlist'
  226.       checkWildCard(frompath)
  227.       scan_directory(frompath,'',1)      -> then process this directory
  228.     ENDWHILE
  229.  
  230.   ENDIF
  231.  
  232. EXCEPT DO  -> Cleanup
  233.  
  234.   IF buffer THEN Dispose(buffer)
  235.  
  236.   IF fastdisplay THEN reportLine(0,'',NIL,NIL)
  237.  
  238.   free_arguments()
  239.   IF rdargs THEN FreeArgs(rdargs)
  240.   IF myrdargs THEN FreeDosObject(DOS_RDARGS,myrdargs)
  241.  
  242.   CloseLibrary(asynciobase)
  243.  
  244.   IF exception
  245.     /* Print error description */
  246.     PrintF('Error: ')
  247.     SELECT exception
  248.       CASE "MEM"  ; PrintF('Not enough memory.\n')
  249.       CASE "addp" ; PrintF('Path too long !?\n')
  250.       CASE "^C"   ; PrintF('User abort.\n')
  251.       CASE "copy" ; PrintF('Could not copy file.\n')
  252.       CASE "open" ; PrintF('Could not open file.\n')
  253.       CASE "anly" ; PrintF('Could not analyse file.\n')
  254.       CASE "fatt" ; PrintF('Could not set file attributes.\n')
  255.       CASE "asio" ; PrintF('Could not open asyncio.library v39.\n')
  256.       DEFAULT     ; PrintF('\s\n',exceptioninfo)
  257.     ENDSELECT
  258.   ELSE
  259.     PrintF('Finished.\n')
  260.   ENDIF
  261.  
  262.   CleanUp(exception)
  263.  
  264. ENDPROC
  265. ->\\
  266.  
  267. CHAR 0,'$VER: UpDateCopy 1.5 (17.03.98)',0
  268.  
  269.  
  270. /* checks the arguments provied by user
  271. */
  272. PROC init_arguments()
  273. ->// "init_arguments()"
  274.  
  275.   IF (dirlock:=Lock(topath,SHARED_LOCK))=NIL            -> check if destination
  276.     Throw("dir",'Could not lock destination directory') -> directory exists
  277.   ENDIF
  278.  
  279.   /* Enable Version Check if datestamp-check is switch off */
  280.   IF Not(checkdates) THEN checkversion:=TRUE
  281.  
  282. ENDPROC
  283. ->\\
  284.  
  285. /* handles so special cases with wildcards
  286. ** Parameter:
  287. **   file - file/dir to be checked
  288. */
  289. PROC checkWildCard(file:PTR TO CHAR)
  290. ->// "checkWildCard"
  291. DEF len
  292.  
  293.   len:=StrLen(file)
  294.   IF file[len-1]="*"
  295.     -> replace '*'-wildcard by '#?'
  296.     CopyMem('#?',file+len-1,3)
  297.   ELSEIF file[len-1]=":"
  298.     -> it's an device. Therefore add wildcard
  299.     CopyMem('#?',file+len,3)
  300.   ENDIF
  301.  
  302. ENDPROC
  303. ->\\
  304.  
  305. /* Cleanup datas allocated during init_arguments
  306. */
  307. PROC free_arguments()
  308. ->// "free_arguments()"
  309.   UnLock(dirlock)             -> Unlock destination directory
  310. ENDPROC
  311. ->\\
  312.  
  313. /* Scans a directory and copies files/creates subdirectories
  314. ** Parameter:
  315. **   directory    - name of directory to be scanned
  316. **   path         - the current delta path
  317. **   depth        - recursion level
  318. ** Example:
  319. **   The source path is "esource:src".
  320. **   'directory' is "esource:src/tools/file".
  321. **   Then 'path' have to be "tools/file".
  322. */
  323. PROC scan_directory(directory,path,depth) HANDLE
  324. ->// "scan_directory()"
  325. DEF info:PTR TO fileinfoblock,
  326.     anchor=NIL:PTR TO anchorpath,
  327.     error,
  328.     fullpath,
  329.     mypath[MAXPATH]:STRING,
  330.     length
  331.  
  332.   /* Create and initialize anchor structure needed for
  333.   ** scanning through directory
  334.   */
  335.   anchor,fullpath:=allocAnchorPath(PATHLENGTH)
  336.  
  337.   error:=MatchFirst(directory,anchor)
  338.   WHILE error=DOSFALSE
  339.  
  340.     CtrlC()
  341.  
  342.     info:=anchor.info                           -> get fileinfoblock
  343.     IF info.direntrytype>0                      -> is it a directory ?
  344.  
  345.       eAddPartC(mypath, path, info.filename)    -> init new delta path including new subdirectory
  346.  
  347.       process_directory(fullpath,info,mypath,   -> knows what to do with this
  348.                         depth*SPACEADD)         -> directory
  349.  
  350.       IF recursiv                               -> scan subdirectories ?
  351.         length:=StrLen(fullpath)
  352.         IF (length+5)<MAXPATH
  353.           CopyMem('/#?',fullpath+length,4)      -> add pattern matching
  354.         ENDIF
  355.         scan_directory(fullpath,mypath,depth+1) -> call our self with new subdirectory
  356.         fullpath[length]:=0                     -> remove pattern matching
  357.       ENDIF
  358.  
  359.     ELSE
  360.  
  361.       process_file(fullpath,info,path,          -> knows what to do with it
  362.                    depth*SPACEADD)
  363.  
  364.     ENDIF
  365.  
  366.     error:=MatchNext(anchor)                    -> Next entry
  367.   ENDWHILE
  368.  
  369. EXCEPT DO
  370.  
  371.   IF anchor
  372.     MatchEnd(anchor)                            -> Clean up
  373.     freeAnchorPath(anchor)
  374.   ENDIF
  375.  
  376.   ReThrow()
  377.  
  378. ENDPROC
  379. ->\\
  380.  
  381. /* takes care of fastdisplay-option
  382. */
  383. PROC reportLine(spaceanz,fmtstr,arg1,arg2,doCR=TRUE)
  384. ->// "reportLine()"
  385.  
  386.   IF fastdisplay
  387.     PrintF([141,$1b,"[","M",0]:CHAR)
  388.     spaceanz:=SPACEADD
  389.   ENDIF
  390.   WHILE spaceanz-->=0 DO FputC(stdout," ")
  391.   PrintF(fmtstr,arg1,arg2)                 -> Write directory name & status
  392.   IF doCR THEN PrintF('\n')
  393.  
  394. ENDPROC
  395. ->\\
  396.  
  397. /* Ask the user what to on error.
  398. ** Returns ASKUSER_xxx
  399. */
  400. PROC askError()
  401. ->// "askError()"
  402. DEF c=0,
  403.     ret=ASKUSER_Abort
  404.  
  405.   IF interaction
  406.  
  407.     PrintF('  (R)etry / (S)kip / (A)bort ? ')
  408.     Flush(stdout)
  409.  
  410.     REPEAT
  411.     UNTIL WaitForChar(stdin,9999999999)=DOSTRUE
  412.     c:=FgetC(stdin)
  413.     Flush(stdin)
  414.  
  415.     IF (c="r") OR (c="R")
  416.       ret:=ASKUSER_Retry
  417.     ELSEIF (c="a") OR (c="A")
  418.       ret:=ASKUSER_Abort
  419.     ELSE
  420.       c:="s"
  421.       ret:=ASKUSER_Skip
  422.     ENDIF
  423.   ENDIF
  424.   ->IF c THEN PrintF('\c',c)
  425.   IF c=0 THEN PrintF('\n')
  426. ENDPROC ret
  427. ->\\
  428.  
  429. /* Prints a directory status.
  430. ** Parameter:
  431. **   spaces - number of leading spaces
  432. **   status - status (PDIR_Error, PDIR_Skipped, PDIR_Created)
  433. **   path   - directory path
  434. */
  435. PROC printDirStatus(spaces,status,path)
  436. ->// "printDirStatus()"
  437. DEF stri
  438.  
  439.   SELECT status
  440.     CASE PDIR_Created ; stri:='created'
  441.     CASE PDIR_Skipped ; IF smartinfo THEN RETURN  -> don't display skipped dirs with smartinfo
  442.                         stri:='skipped'
  443.     DEFAULT           ; stri:='Error!!'
  444.   ENDSELECT
  445.  
  446.   -> Write directory name & status
  447.   IF doinfo OR (status=PDIR_Error)
  448.     reportLine(spaces,'\e[1m\s\e[0m..\s',path,stri,status<>PDIR_Error)
  449.   ENDIF
  450.  
  451. ENDPROC
  452. ->\\
  453.  
  454. /* Process an directory. If it doesnt exists it will be created
  455. ** Parameter:
  456. **  directory - full path of source directory
  457. **  info      - pointer to fileinfoblock of source directory
  458. **  path      - delta path of directory
  459. */
  460. PROC process_directory(directory,info:PTR TO fileinfoblock,path,spaces)
  461. ->// "process_directory()"
  462. DEF stri[MAXPATH]:STRING,
  463.     lock
  464.  
  465.   eAddPartC(stri,topath,path)                    -> thats the destion-directory
  466.  
  467.   IF checkarchivebit AND
  468.      (info.protection AND FIBF_ARCHIVE)          -> Archive-bit set ?
  469.     printDirStatus(spaces, PDIR_Skipped, path)
  470.  
  471.   ELSEIF lock:=Lock(stri,SHARED_LOCK)            -> Exists this Directory ?
  472.     printDirStatus(spaces, PDIR_Skipped, path)
  473.     UnLock(lock)                                 -> Unlock directory
  474.     IF setarchivebit THEN SetProtection(directory,info.protection OR FIBF_ARCHIVE)
  475.  
  476.   ELSEIF testmode                                -> Don't create directory in testmode
  477.     printDirStatus(spaces, PDIR_Created, path)   -> but write out a info
  478.  
  479.   ELSEIF lock:=needThisDir(stri)                 -> Create the directory
  480.     printDirStatus(spaces, PDIR_Created, path)
  481.     UnLock(lock)                                 -> No erros, unlock directory
  482.     copyAdditionalInformations(stri,info)
  483.     IF setarchivebit THEN SetProtection(directory,info.protection OR FIBF_ARCHIVE)
  484.  
  485.   ELSE                                           -> Directory could not be created
  486.     printDirStatus(spaces, PDIR_Error, path)     -> as it does not exists before
  487.     lock:=askError()
  488.     SELECT lock
  489.       CASE ASKUSER_Abort
  490.         Throw("dir",'Could not create directory')-> something went wrong
  491.       CASE ASKUSER_Retry
  492.         process_directory(directory,info,path,spaces)
  493.     ENDSELECT
  494.   ENDIF
  495.  
  496. ENDPROC
  497. ->\\
  498.  
  499. /*
  500. ** Truncates the tick-entry of the datestamp to be a multiple of 100.
  501. ** (lha does this!!)
  502. ** It seems they transform the ticks into seconds (/50) and made
  503. ** them a multiple of 2.
  504. */
  505. PROC simplifyDateStamp(ds:PTR TO datestamp)
  506. ->// "simplifyDateStamp"
  507.   ds.tick:=ds.tick/100*100
  508. ENDPROC
  509. ->\\
  510.  
  511. /*
  512. ** Gets the PathPart of an string.
  513. ** 'dst' must be an EString!
  514. */
  515. PROC getPath(dst, stri) IS StrCopy(dst, stri, PathPart(stri)-stri)
  516.  
  517. /* Checks if an directory already exists.
  518. ** If not it (and all necessary parent directories) are created.
  519. ** Parameter:
  520. **   stri - full name of the directory to be created
  521. ** Returns a lock to the directory or NIL in case of an error.
  522. */
  523. PROC needThisDir(stri)
  524. ->// "needThisDir()"
  525. DEF lock, hstri[MAXPATH]:STRING
  526.  
  527.   ->WriteF('need dir >>\s<<\n',stri)
  528.   CtrlC()
  529.  
  530.   IF lock:=Lock(stri, SHARED_LOCK)
  531.     -> Already exists
  532.   ELSEIF lock:=needThisDir(getPath(hstri, stri))
  533.     -> All parent directories exists
  534.     UnLock(lock)
  535.     lock:=CreateDir(stri)
  536.   ENDIF
  537.  
  538. ENDPROC lock
  539. ->\\
  540.  
  541. /* Prints a file status.
  542. ** Parameter:
  543. **   spaces - number of leading spaces
  544. **   status - status (PFILE_Error, PFILE_Skipped, PFILE_Created, PFILE_Skipped)
  545. **   file   - filename
  546. */
  547. PROC printFileStatus(spaces,status,file)
  548. ->// "printFileStatus()"
  549. DEF stri
  550.  
  551.   SELECT status
  552.     CASE PFILE_Copied   ; stri:='copied'
  553.     CASE PFILE_Replaced ; stri:='replaced'
  554.     CASE PFILE_Skipped  ; IF smartinfo THEN RETURN -> don't display skipped dirs with smartinfo
  555.                           stri:='skipped'
  556.     DEFAULT             ; stri:='Error!!'
  557.   ENDSELECT
  558.   -> Write file status
  559.   IF doinfo OR (status=PFILE_Error)
  560.     reportLine(spaces,'\s..\s',file,stri,status<>PFILE_Error)
  561.   ENDIF
  562.  
  563. ENDPROC
  564. ->\\
  565.  
  566. /* Processes an file. If it does not exists or is newer it is copied.
  567. ** Parameter:
  568. **   file  - full source filename (include path)
  569. **   info  - ptr to fileinfoblock of sourcefile
  570. **   path  - deltapath to directory (exclude filename !)
  571. */
  572. PROC process_file(file,info:PTR TO fileinfoblock,path,spaces) HANDLE
  573. ->// "process_file()"
  574. DEF stri[MAXPATH]:STRING,
  575.     filepath[MAXPATH]:STRING,
  576.     fh=NIL,
  577.     toinfo=NIL:PTR TO fileinfoblock,
  578.     result=0,
  579.     frombuf=NIL,
  580.     lock,
  581.     hstri[MAXPATH]:STRING
  582.  
  583.   eAddPartC(filepath,path,info.filename)                 -> create deltapath inclusive filename
  584.   eAddPartC(stri,topath,filepath)                        -> create full destination filepath
  585.  
  586.   IF checkarchivebit AND
  587.      (info.protection AND FIBF_ARCHIVE)                  -> Archive-bit set
  588.  
  589.     -> Do not copy already archived files
  590.     printFileStatus(spaces, PFILE_Skipped, filepath)
  591.  
  592.   ELSEIF fh:=Open(stri,MODE_OLDFILE)                     -> Open destinationfile
  593.  
  594.     IF checkversion THEN result,frombuf:=compareversion(file,stri)
  595.  
  596.     IF checkdates AND (result=0)
  597.       /* Fileinfoblock have to be LONGWORD-aligned therefore
  598.       ** use dos.library to create this
  599.       */
  600.       toinfo:=AllocDosObject(DOS_FIB,NIL)
  601.  
  602.       -> fill fileinfoblock
  603.       IF ExamineFH(fh,toinfo)=DOSFALSE THEN Throw("file",'Could not examine file ?!')
  604.  
  605.       /* lha compatible datestamp compare ?
  606.       */
  607.       IF lousydatestamp
  608.         simplifyDateStamp(info.datestamp)
  609.         simplifyDateStamp(toinfo.datestamp)
  610.       ENDIF
  611.  
  612.       /* Compare versionstrings was either not specified by user
  613.       ** or was not successfull. Therefore Compare filedates if
  614.       ** the user want it.
  615.       */
  616.       result:=CompareDates(toinfo.datestamp,info.datestamp)
  617.     ENDIF
  618.  
  619.     IF result>0                                        -> fromfile newer than tofile ?
  620.       Close(fh) ; fh:=NIL                              -> close destination
  621.  
  622.       IF Not(testmode)
  623.         IF ignoreprotection THEN SetProtection(stri,0) -> Clear protectionflags if specified
  624.         copyfile(file,info,stri,frombuf)               -> copy the file
  625.       ENDIF
  626.  
  627.       printFileStatus(spaces, PFILE_Replaced, filepath)
  628.     ELSE
  629.       printFileStatus(spaces, PFILE_Skipped, filepath)
  630.     ENDIF
  631.  
  632.   ELSEIF testmode                                      -> destination does not exists but testmode
  633.     printFileStatus(spaces, PFILE_Copied, filepath)
  634.  
  635.   ELSEIF lock:=needThisDir(getPath(hstri, stri))       -> destination does not exists
  636.     UnLock(lock)
  637.     copyfile(file,info,stri)                           -> copy file
  638.     printFileStatus(spaces, PFILE_Copied, filepath)
  639.   ENDIF
  640.  
  641. EXCEPT DO  -> Cleanup
  642.  
  643.   IF toinfo THEN FreeDosObject(DOS_FIB,toinfo)
  644.   IF fh THEN Close(fh)
  645.  
  646.   IF exception
  647.     printFileStatus(spaces, PFILE_Error, filepath)
  648.     result:=askError()
  649.     SELECT result
  650.       CASE ASKUSER_Abort
  651.         ReThrow()            -> something went wrong
  652.       CASE ASKUSER_Retry
  653.         process_file(file,info,path,spaces)
  654.     ENDSELECT
  655.   ENDIF
  656.  
  657. ENDPROC
  658. ->\\
  659.  
  660.  
  661. /* Copies a file. For files >BIGFILESIZE Bytes c:copy is
  662. ** used.
  663. ** Parameter:
  664. **  fromfile  - full path of sourcefile
  665. **  tofile    - full path of destinationfile
  666. **  frombuf   - Contents of sourcefile. If NIL then sourcefile
  667. **              is read
  668. */
  669. PROC copyfile(fromfile,frominfo:PTR TO fileinfoblock,tofile,frombuf=NIL) HANDLE
  670. ->// "copyfile()"
  671. DEF fhfrom=NIL,
  672.     fhto=NIL,
  673.     length,
  674.     steplength,actlength
  675.  
  676.   IF ((fhfrom:=OpenAsync(fromfile,MODE_READ,ASYNCBUFFER))<>NIL) AND    -> open sourcefile
  677.      ((fhto:=OpenAsync(tofile,MODE_WRITE,ASYNCBUFFER))<>NIL)            -> open destinationfile
  678.  
  679.     IF (length:=FileLength(fromfile))>BIGFILESIZE
  680.       /* Files >BIGFILESIZE are not read completly into memory but in parts
  681.       ** of BIGFILESIZE Bytes.
  682.       */
  683.  
  684.       /* steplength is the length of the current block to be read */
  685.       /* actlength is the position within the file */
  686.       steplength:=actlength:=BIGFILESIZE
  687.  
  688.       REPEAT
  689.         /* Read next block to buffer */
  690.         IF ReadAsync(fhfrom,buffer,steplength)<>steplength THEN Raise("copy")
  691.  
  692.         /* Write buffer to destinationfile */
  693.         IF WriteAsync(fhto,buffer,steplength)<>steplength THEN Raise("copy")
  694.         
  695.         IF (actlength:=actlength+BIGFILESIZE)>length
  696.           steplength:=length-(actlength-BIGFILESIZE)
  697.         ENDIF
  698.  
  699.         /* Read until version-string was found or EOF */
  700.       UNTIL steplength<=0
  701.  
  702.     ELSE
  703.  
  704.       IF frombuf                               -> we already know the
  705.                                                -> contents of the sourcefile
  706.         /* Write buffer to destinationfile */
  707.         IF WriteAsync(fhto,frombuf,length)<>length THEN Raise("copy")
  708.       ELSE
  709.  
  710.         /* Read sourcefile into buffer */
  711.         IF ReadAsync(fhfrom,buffer,length)<>length THEN Raise("copy")
  712.         /* Write buffer TO destinationfile */
  713.         IF WriteAsync(fhto,buffer,length)<>length THEN Raise("copy")
  714.       ENDIF
  715.  
  716.     ENDIF
  717.   ELSE                                       -> There went something wrong
  718.     Raise("copy")
  719.   ENDIF
  720.  
  721. EXCEPT DO
  722.  
  723.   IF fhfrom THEN CloseAsync(fhfrom)
  724.   IF fhto THEN CloseAsync(fhto)
  725.  
  726.   ReThrow()
  727.  
  728.   /* Everything went ok. Copy comment, protection flags etc. */
  729.   copyAdditionalInformations(tofile,frominfo)
  730.   IF setarchivebit THEN SetProtection(fromfile,frominfo.protection OR FIBF_ARCHIVE)
  731.  
  732. ENDPROC
  733. ->\\
  734.  
  735. /* Copies extra informations like protectionbits, comment etc.
  736. ** 'name' must be a valid File- or Directory path
  737. ** 'info' is the fileinfoblock of the original file/directories
  738. **        (the dates are copied from it)
  739. **
  740. ** Note: the file may not be opened or locked!
  741. */
  742. PROC copyAdditionalInformations(name,info:PTR TO fileinfoblock)
  743. ->// "copyAdditionalInformations()"
  744.  
  745. DEF protection
  746.  
  747.   /* Clear protectionbit (or not) */
  748.   protection:=info.protection
  749.   IF cleararchivebit THEN protection:=protection AND Not(FIBF_ARCHIVE)
  750.  
  751.   SetProtection(name,protection)       -> Copy protection bits
  752.   SetComment(name,info.comment)        -> Copy comment
  753.  
  754.   /* Copy datestamp if clone option is activated */
  755.   IF clone THEN SetFileDate(name,info.datestamp)
  756.  
  757. ENDPROC
  758. ->\\
  759.  
  760.  
  761. /* Compares the version-strings of two files
  762. ** Parameter:
  763. **   fromfile - full path of sourcefile
  764. **   tofile   - full path of destinationfile
  765. **
  766. ** Returns
  767. **   -1 if tofile-version>=fromfile-version
  768. **    0 on error
  769. **    1 if               <
  770. ** *AND* the contents of the sourcefile or NIL
  771. */
  772. PROC compareversion(fromfile,tofile)
  773. ->// "compareversion()"
  774. DEF frombuf:PTR TO CHAR,
  775.     version1,base1,
  776.     version2,base2
  777.  
  778.   /* We dont need the destination file. Therefore don't get buffer.
  779.   */
  780.   version2,base2:=getVersionOfFile(tofile)
  781.  
  782.   /* if version is -1 then no version-string was found */
  783.   IF version2=-1 THEN RETURN 0,NIL
  784.  
  785.   version1,base1,frombuf:=getVersionOfFile(fromfile)
  786.   IF version1=-1 THEN RETURN 0,frombuf
  787.  
  788.   /* Compare the version, reversion of source/destionation
  789.   ** multiply with other base to get same number of signifant numbers
  790.   */
  791.   RETURN IF (version2*base1)>=(version1*base2) THEN -1 ELSE 1,frombuf
  792.  
  793. ENDPROC
  794. ->\\
  795.  
  796.  
  797. /* Gets the version-number of a file
  798. ** Parameter:
  799. **   filename - full path of file
  800. ** Returns the version, base and a buffer with the contents of the file
  801. ** (description for version,base see getversion())
  802. ** buffer may be NIL if file was to large
  803. */
  804. PROC getVersionOfFile(filename) HANDLE
  805. ->// "getVersionOfFile()"
  806. DEF fh,
  807.     frombuf=NIL,
  808.     version,base,
  809.     steplength,filelength,actlength
  810.  
  811.   IF (fh:=OpenAsync(filename,MODE_READ,ASYNCBUFFER))=NIL THEN Raise("open")
  812.  
  813.   filelength:=FileLength(filename)
  814.   IF filelength>BIGFILEMEM
  815.     /* Files >BIGFILESIZE are not read completly into memory but in parts
  816.     ** of BIGFILESIZE Bytes.
  817.     ** As we may skip a versionstring the last SAFETYBYTES are copied
  818.     ** to start of new BIGFILESIZE block everytime.
  819.     */
  820.  
  821.     /* Read filecontents into buffer */
  822.     IF ReadAsync(fh,buffer,BIGFILEMEM)<>BIGFILEMEM THEN Raise("anly")
  823.  
  824.     /* steplength is the length of the current block to be read */
  825.     steplength:=BIGFILESIZE
  826.     /* actlength is the position within the file */
  827.     actlength:=BIGFILEMEM
  828.  
  829.     REPEAT
  830.  
  831.       /* Get version. If version=-1 then read next block */
  832.       version,base:=getversion(buffer,steplength+SAFETYBYTES)
  833.       IF version=-1
  834.  
  835.         /* The version-string was maybe at the end of buffer
  836.         ** and therfore skipped. Copy the last SAFETYBYTES bytes
  837.         ** to start of buffer to not lose the version-string.
  838.         */
  839.         CopyMem(buffer+steplength,buffer,SAFETYBYTES)
  840.  
  841.         /* Increase actlength. If actlength is greater than filelength
  842.         ** then set steplength to the number of bytes left.
  843.         */
  844.         actlength:=actlength+BIGFILESIZE
  845.         IF actlength>filelength
  846.           steplength:=filelength-(actlength-BIGFILESIZE)
  847.         ENDIF
  848.  
  849.         /* Read next block to buffer. If steplength<0 then EOF is reached */
  850.         IF steplength>0
  851.           IF ReadAsync(fh,buffer+SAFETYBYTES,steplength)<>steplength THEN Raise("anly")
  852.         ENDIF
  853.  
  854.       ENDIF
  855.  
  856.       /* Read until version-string was found or EOF */
  857.     UNTIL (version<>-1) OR (steplength<=0)
  858.  
  859.   ELSE
  860.     /* We have got a small file. Load it completly into memory.
  861.     */
  862.     IF ReadAsync(fh,buffer,filelength)<>filelength THEN Raise("anly")
  863.  
  864.     /* Notify that we have the file already loaded
  865.     */
  866.     frombuf:=buffer
  867.  
  868.     /* get versions and reversion of sourcefile */
  869.     version,base:=getversion(buffer,filelength)
  870.   ENDIF
  871.  
  872.  
  873. EXCEPT DO
  874.  
  875.   /* Close the filehandle */
  876.   IF fh THEN CloseAsync(fh)
  877.   ReThrow()
  878.  
  879. ENDPROC version,base,frombuf
  880. ->\\
  881.  
  882.  
  883. /* Search for a version-string in a file
  884. ** Parameter:
  885. **   buffer       - Contents of file
  886. **   bufferlength - length of buffer in bytes
  887. ** Returns version,base
  888. ** if no version-string was found then -1 is returned as version
  889. ** Example: version=81259, base=10000 means
  890. **          Versionnumber is 81259/10000=8.1259
  891. */
  892. PROC getversion(buffer:PTR TO CHAR,bufferlength)
  893. ->// "getversion()"
  894. -> Works with up to 2Gig
  895. DEF version:REG,base:REG
  896.  
  897.       MOVEA.L buffer,A0           -> A0..buffer
  898.       MOVE.L  bufferlength,D0     -> D0..bufferlength
  899.       MOVEQ   #"$",D1
  900.       MOVE.L  #"VER:",D2
  901. gv_search_loop:
  902.       SUBQ.L  #1,D0
  903.       BLT.S   gv_error            -> bufferend reached ?
  904.       CMP.B   (A0)+,D1
  905.       BNE.S   gv_search_loop      -> Found a "$" ?
  906.       CMP.L   (A0),D2             -> Yes, then next characters="VER:"
  907.       BNE.S   gv_search_loop
  908.  
  909.       SUBQ.L  #4,D0               -> We have found a version-string
  910.       BLT.S   gv_error
  911.       ADDQ.L  #4,A0               -> skip "VER:"
  912.  
  913.       MOVEQ   #" ",D1
  914. gv_skipspaces1:                   -> skip all spaces before programname
  915.       SUBQ.L  #1,D0
  916.       BLT.S   gv_error            -> bufferend reached ?
  917.       CMP.B   (A0)+,D1
  918.       BEQ.S   gv_skipspaces1
  919.       ADDQ.L  #1,D0               -> all spaces skipped; we have gone
  920.       SUBQ.L  #1,A0               -> one step too far.
  921.  
  922. gv_skipname:                      -> skip programname (find next space)
  923.       SUBQ.L  #1,D0
  924.       BLT.S   gv_error            -> bufferend reached ?
  925.       CMP.B   (A0)+,D1
  926.       BNE.S   gv_skipname
  927.       ADDQ.L  #1,D0               -> go one step back
  928.       SUBQ.L  #1,A0
  929.  
  930. gv_skipspaces2:                   -> skip spaces before version-number
  931.       SUBQ.L  #1,D0
  932.       BLT.S   gv_error            -> bufferend reached ?
  933.       CMP.B   (A0)+,D1
  934.       BEQ.S   gv_skipspaces2
  935.       ADDQ.L  #1,D0               -> go one step back
  936.       SUBQ.L  #1,A0
  937.  
  938.       MOVEQ   #0,version
  939.       MOVEQ   #".",D1
  940.       MOVEQ   #0,D2
  941. gv_getversion1:                   -> get version until we found a "."
  942.       SUBQ.L  #1,D0
  943.       BLT.S   gv_error            -> bufferend reached ?
  944.       MOVE.B  (A0)+,D2
  945.       SUBI.B  #"0",D2             -> Transform character "0".."9" into number
  946.       MULU.W  #10,version         -> version:=version*10
  947.       ADD.L   D2,version          ->                    +number
  948.       CMP.B   (A0),D1
  949.       BNE.S   gv_getversion1
  950.       SUBQ.L  #1,D0               -> skip "."
  951.       ADDQ.L  #1,A0
  952.  
  953.       MOVEQ   #" ",D1
  954.       MOVEQ   #1,base
  955. gv_getversion2:                   -> get reversion
  956.       SUBQ.L  #1,D0
  957.       BLT.S   gv_error            -> bufferend reached ?
  958.       MOVE.B  (A0)+,D2
  959.       SUBI.B  #"0",D2             -> Transform character "0".."9" into number
  960.       MULU.W  #10,version         -> version:=version*10
  961.       MULU.W  #10,base            -> base:=base*10
  962.       ADD.L   D2,version          ->                    +number
  963.       CMP.B   (A0),D1
  964.       BNE.S   gv_getversion2
  965.       BRA.S   gv_return
  966.  
  967. gv_error:
  968.       MOVEQ   #-1, version        -> an error occured (bufferend reached)
  969.  
  970. gv_return:
  971.  
  972. ENDPROC version,base
  973. ->\\
  974.  
  975.